package com.atguigu.gmall.search.service.impl;
import cn.hutool.core.util.PageUtil;
import com.atguigu.gmall.search.entity.SearchAttr;
import com.atguigu.gmall.search.vo.SearchRespAttrVo;
import com.atguigu.gmall.search.vo.SearchTmVo;
import com.google.common.collect.Lists;
import com.atguigu.gmall.search.vo.SearchOrderMapVo;

import com.atguigu.gmall.search.dto.SearchParamDto;
import com.atguigu.gmall.search.entity.Goods;
import com.atguigu.gmall.search.repository.GoodsRepository;
import com.atguigu.gmall.search.service.GoodsService;
import com.atguigu.gmall.search.vo.SearchResponseVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Slf4j
public class GoodsServiceImpl implements GoodsService {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate ;

    @Autowired
    private GoodsRepository goodsRepository ;

    @Override
    public void saveGoods(Goods goods) {
        goodsRepository.save(goods) ;
    }

    @Override
    public void deleteById(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    @Override
    public SearchResponseVo search(SearchParamDto searchParamDto) {

        // 构建的搜索的请求参数对象
        NativeSearchQuery nativeSearchQuery = buildNativeSearchQuery(searchParamDto);

        // 发送搜索请求
        SearchHits<Goods> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Goods.class);

        // 解析响应对象获取结果数据
        SearchResponseVo searchResponseVo = parseSearchHits(searchHits , searchParamDto) ;

        // 返回
        return searchResponseVo;
    }

    @Override
    public void updateHotScore(Long skuId, Long hotScore) {
        Optional<Goods> optional = goodsRepository.findById(skuId);
        Goods goods = optional.get();
        goods.setHotScore(hotScore);
        goodsRepository.save(goods) ;
    }

    private SearchResponseVo parseSearchHits(SearchHits<Goods> searchHits, SearchParamDto searchParamDto) {

        // 创建一个SearchResponseVo对象
        SearchResponseVo searchResponseVo = new SearchResponseVo() ;

        // 搜索条件参数对象封装
        searchResponseVo.setSearchParam(searchParamDto);

        // 品牌面包屑
        String trademark = searchParamDto.getTrademark();
        if(!StringUtils.isEmpty(trademark)) {
            String[] trademarkArr = trademark.split(":");
            searchResponseVo.setTrademarkParam("品牌：" + trademarkArr[1]);
        }

        // 品牌属性面包屑
        String[] props = searchParamDto.getProps();
        if(props != null && props.length > 0) {
            List<SearchAttr> searchAttrList = new ArrayList<>() ;
            for(String prop : props) {
                String[] propArr = prop.split(":");
                Long attrId = Long.parseLong(propArr[0]) ;
                String attrValue = propArr[1] ;
                String attrName = propArr[2] ;
                SearchAttr searchAttr = SearchAttr.builder()
                        .attrId(attrId)
                        .attrName(attrName)
                        .attrValue(attrValue).build();
                searchAttrList.add(searchAttr);
            }
            searchResponseVo.setPropsParamList(searchAttrList);
        }

        // 设置排序参数对象
        String order = searchParamDto.getOrder();       // 1:desc
        SearchOrderMapVo searchOrderMapVo = new SearchOrderMapVo("1" , "desc") ;
        if(!StringUtils.isEmpty(order) && !"null".equals(order)) {
            String[] orderArr = order.split(":");
            String type = orderArr[0] ;
            String sort = orderArr[1] ;
            searchOrderMapVo = SearchOrderMapVo.builder()
                    .type(type)
                    .sort(sort)
                    .build();
        }
        searchResponseVo.setOrderMap(searchOrderMapVo);

        // 设置当前页数据
        List<SearchHit<Goods>> searchHitList = searchHits.getSearchHits();
        List<Goods> goodsList = searchHitList.stream().map(searchHit -> {
            Goods goods = searchHit.getContent();
            Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
            if(highlightFields != null && highlightFields.size() > 0) {
                List<String> list = highlightFields.get("title");
                if(list != null && list.size() > 0) {
                    String highlightTitle = list.get(0);
                    goods.setTitle(highlightTitle);
                }
            }
            return goods ;
        }).collect(Collectors.toList());
        searchResponseVo.setGoodsList(goodsList);

        // 设置分页参数
        searchResponseVo.setPageNo(searchParamDto.getPageNo());
        Long totalHits = searchHits.getTotalHits();
        int totalPage = PageUtil.totalPage(totalHits.intValue(), searchParamDto.getPageSize());
        searchResponseVo.setTotalPages(totalPage);

        // 解析品牌聚合结果
        Aggregations aggregations = searchHits.getAggregations();
        ParsedLongTerms tmIdAgg = aggregations.get("tmIdAgg");
        List<? extends Terms.Bucket> buckets = tmIdAgg.getBuckets();
        List<SearchTmVo> searchTmVoList = new ArrayList<>() ;
        for(Terms.Bucket bucket : buckets) {

            // 品牌的id
            String tmIdStr = bucket.getKeyAsString();

            // 获取子聚合结果
            Aggregations bucketAggregations = bucket.getAggregations();
            ParsedStringTerms tmNameparsedStringTerms = bucketAggregations.get("tmNameAgg") ;
            String tmName = tmNameparsedStringTerms.getBuckets().get(0).getKeyAsString();

            // 获取tmLogoUrl
            ParsedStringTerms tmLogoUrlAggParsedStringTerms =  bucketAggregations.get("tmLogoUrlAgg") ;
            String tmLogoUrl = tmLogoUrlAggParsedStringTerms.getBuckets().get(0).getKeyAsString();

            // 封装数据
            SearchTmVo searchTmVo = SearchTmVo.builder().tmId(Long.parseLong(tmIdStr))
                    .tmName(tmName)
                    .tmLogoUrl(tmLogoUrl)
                    .build();

            // 把searchTmVo存储到searchTmVoList中
            searchTmVoList.add(searchTmVo);
        }
        searchResponseVo.setTrademarkList(searchTmVoList);

        // 构建urlParam
        String urlParam = buildUrlParam(searchParamDto) ;
        searchResponseVo.setUrlParam(urlParam);

        // 构建搜索面板的平台属性数据
        ParsedNested attrsAggAggregation = aggregations.get("attrsAgg");
        Aggregations attrsAggAggregations = attrsAggAggregation.getAggregations();
        ParsedLongTerms parsedLongTerms = attrsAggAggregations.get("attrIdAgg");
        List<? extends Terms.Bucket> termsBuckets = parsedLongTerms.getBuckets();
        List<SearchRespAttrVo> searchRespAttrVoList = new ArrayList<>() ;
        for(Terms.Bucket bucket : termsBuckets) {

            // 获取平台属性id
            String attrIdStr = bucket.getKeyAsString();

            // 获取平台属性名称
            Aggregations bucketAggregations = bucket.getAggregations();
            ParsedStringTerms attrNameAgg = bucketAggregations.get("attrNameAgg") ;
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();

            // 获取平台属性值
            ParsedStringTerms attrValueAgg = bucketAggregations.get("attrValueAgg") ;
            List<? extends Terms.Bucket> aggBuckets = attrValueAgg.getBuckets();
            List<String> attrValueList = new ArrayList<>() ;
            for(Terms.Bucket aggBucket : aggBuckets) {
                String attrValue = aggBucket.getKeyAsString();
                attrValueList.add(attrValue) ;
            }

            // 封装平台属性数据
            SearchRespAttrVo searchRespAttrVo = SearchRespAttrVo.builder()
                    .attrId(Long.parseLong(attrIdStr))
                    .attrName(attrName)
                    .attrValueList(attrValueList)
                    .build();

            // 把数据存储到searchRespAttrVoList对象
            searchRespAttrVoList.add(searchRespAttrVo) ;

        }
        searchResponseVo.setAttrsList(searchRespAttrVoList);

        // 返回
        return searchResponseVo ;

    }

    // 构建urlParam参数
    private String buildUrlParam(SearchParamDto searchParamDto) {

        // 创建StringBuilder
        StringBuilder sb = new StringBuilder("list.html?") ;

        // 拼接三级分类的请求参数
        Long category1Id = searchParamDto.getCategory1Id();
        if(category1Id != null) {
            sb.append("&category1Id=").append(category1Id) ;
        }

        Long category2Id = searchParamDto.getCategory2Id();
        if(category2Id != null) {
            sb.append("&category2Id=").append(category2Id) ;
        }

        Long category3Id = searchParamDto.getCategory3Id();
        if(category3Id != null) {
            sb.append("&category3Id=").append(category3Id) ;
        }

        // 拼接keyword的请求参数
        String keyword = searchParamDto.getKeyword();
        if(!StringUtils.isEmpty(keyword)) {
            sb.append("&keyword=").append(keyword) ;
        }

        // 拼接品牌的搜索条件
        String trademark = searchParamDto.getTrademark();
        if(!StringUtils.isEmpty(trademark)) {
            sb.append("&trademark=").append(trademark) ;
        }

        // 拼接平台属性的搜索条件
        String[] props = searchParamDto.getProps();
        if(props != null && props.length > 0) {
            for(String prop : props) {
                sb.append("&props=").append(prop) ;
            }
        }

        // 返回
        return sb.toString() ;

    }

    public NativeSearchQuery buildNativeSearchQuery(SearchParamDto searchParamDto) {

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 设置分类id的搜索条件
        Long category1Id = searchParamDto.getCategory1Id();
        if(category1Id != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("category1Id" , category1Id)) ;
        }

        Long category2Id = searchParamDto.getCategory2Id();
        if(category2Id != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("category2Id" , category2Id)) ;
        }

        Long category3Id = searchParamDto.getCategory3Id();
        if(category3Id != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("category3Id" , category3Id)) ;
        }

        // 设置关键字搜索条件
        String keyword = searchParamDto.getKeyword();
        if(!StringUtils.isEmpty(keyword)) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("title" , keyword)) ;
        }

        // 设置品牌的搜索条件
        String trademark = searchParamDto.getTrademark();
        if(!StringUtils.isEmpty(trademark)) {
            String[] trademarkArr = trademark.split(":");
            Long tmId = Long.parseLong(trademarkArr[0]) ;
            boolQueryBuilder.must(QueryBuilders.termQuery("tmId" , tmId)) ;
        }

        // 设置平台属性的搜索条件
        String[] props = searchParamDto.getProps();
        if(props != null && props.length > 0) {
            for(String prop : props) {
                String[] propArr = prop.split(":");
                Long attrId = Long.parseLong(propArr[0]) ;
                String attrValue = propArr[1] ;
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                boolQuery.must(QueryBuilders.termQuery("attrs.attrId" , attrId)) ;
                boolQuery.must(QueryBuilders.termQuery("attrs.attrValue" , attrValue)) ;
                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs", boolQuery, ScoreMode.None);
                boolQueryBuilder.must(nestedQueryBuilder) ;
            }
        }

        // 构建排序参数
        String order = searchParamDto.getOrder();
        FieldSortBuilder sortBuilder = new FieldSortBuilder("hotScore");
        if(!StringUtils.isEmpty(order) && !"null".equals(order)) {
            String[] orderArr = order.split(":");
            String sortField = orderArr[0] ;
            String sortOrder = orderArr[1] ;
            switch (sortField) {
                case "1" :
                    sortBuilder = new FieldSortBuilder("hotScore");
                    break;
                case "2" :
                    sortBuilder = new FieldSortBuilder("price");
                    break;
            }
            SortOrder sortOrderEnum = "asc".equalsIgnoreCase(sortOrder) ? SortOrder.ASC : SortOrder.DESC ;
            sortBuilder.order(sortOrderEnum) ;
        }

        // 创建PageRequest对象，封装分页参数
        PageRequest pageRequest = PageRequest.of(searchParamDto.getPageNo() - 1, searchParamDto.getPageSize());

        // 构建高亮的参数
        HighlightBuilder highlightBuilder = new HighlightBuilder() ;
        highlightBuilder.field("title") ;
        highlightBuilder.preTags("<font color='red'>") ;
        highlightBuilder.postTags("</font>") ;

        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withSort(sortBuilder)
                .withPageable(pageRequest)
                .withHighlightBuilder(highlightBuilder)
                .build();

        // 设置搜索面板品牌数据所需要的聚合参数
        TermsAggregationBuilder tmIdAggregationBuilder = AggregationBuilders.terms("tmIdAgg").field("tmId").size(100);
        TermsAggregationBuilder tmNameAggregationBuilder = AggregationBuilders.terms("tmNameAgg").field("tmName").size(1);
        TermsAggregationBuilder tmLogoUrlAggregationBuilder = AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl").size(1);
        tmIdAggregationBuilder.subAggregation(tmNameAggregationBuilder) ;
        tmIdAggregationBuilder.subAggregation(tmLogoUrlAggregationBuilder) ;
        nativeSearchQuery.addAggregation(tmIdAggregationBuilder);

        // 设置搜索面板平台属性数据所需要的聚合参数
        NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("attrsAgg", "attrs");
        TermsAggregationBuilder attrIdAggAggregationBuilder = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId").size(50);
        attrIdAggAggregationBuilder.subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName").size(1)) ;
        attrIdAggAggregationBuilder.subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue").size(50)) ;
        nestedAggregationBuilder.subAggregation(attrIdAggAggregationBuilder) ;
        nativeSearchQuery.addAggregation(nestedAggregationBuilder);

        return nativeSearchQuery ;
    }

}
